package csm.marc;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.output.XMLOutputter;

import csm.common.SMException;
import csm.common.StringUtil;
import csm.common.TypeConv;
import csm.common.xml.XmlDocument;
//import csm.common.*;
//import csm.common.xml.*;
import csm.marc.extension.*;

/**
 * 마크 파서
 *
 * @author seungmun
 * @since 2006.1.18.
 */
public final class MarcRecord
{
	private MarcType marcType = MarcType.iKORMARC;
	private String matrType = MaterialTypeConstant.MISSING;
	private String leader;

	private ArrayList<Tag> tags = new ArrayList<Tag>();
	private ArrayList<Integer> tagList = new ArrayList<Integer>();

	private UnsupportedCharsHandler handler = UnsupportedCharsHandler.getUnsupportedCharsHandler();

	private int maxKey;

	/**
	 * 현재 마크의 마크유형
	 *
	 * @return MarcType
	 */
	public MarcType getMarcType()
	{
		return marcType;
	}

	/**
	 * Material Type 을 리턴한다.
	 *
	 * @return
	 */
	public String getMatrType()
	{
		return matrType;
	}

	/**
	 * Material Type 을 리턴한다.
	 *
	 * @param isRecalculate
	 *            자료유형정보를 얻기위해 마크를 다시 계산할 지 여부.
	 *            <dl>
	 *            <dt>true</dt>
	 *            <dd>다시 계산함</dd>
	 *            <dt>false</dt>
	 *            <dd>다시 계산하지 않고, 생성자를 이용하여 체크한 유형을 반환</dd>
	 *            </dl>
	 * @return
	 */
	public String getMatrType(boolean isRecalculate)
	{
		if (isRecalculate || matrType == MaterialTypeConstant.MISSING)
		{
			// 기본생성자를 통해 생성한 뒤, 태그 추가식으로 생성한 마크자료의 자료유형은 계산이 되질 않기 때문에
			// 여기서 체크해서 자료유형정보를 생성한다.
			matrType = getMaterialType(this.leader, this.marcType);
		}
		return matrType;
	}

	/**
	 * 마크리더 문자열을 가져옵니다.
	 *
	 * @return
	 */
	public String getLeader()
	{
		return leader;
	}

	/**
	 * 리더 문자열을 설정합니다.
	 *
	 * @param leader
	 *            설정할 문자열입니다. <blockquote> 할당된 문자열이 빈 문자열(empty string)인 경우는 아무런
	 *            작업을 하지 않습니다.<br>
	 *            할당된 문자열의 길이가 <b>24 문자</b>가 아니라면 아무런 작업을 하지 않습니다. </blockquote>
	 */
	public void setLeader(String leader)
	{
		if (StringUtil.isNullOrEmpty(leader)) return;

		if (leader.length() != 24) return;

		this.leader = leader;

		// 리더가 변경되면, 마크유형에 따라 자료유형을 재계산해야 한다.
		matrType = getMaterialType(this.leader, this.marcType);
	}

	/**
	 * 해당 태그가 존재하는지 여부
	 *
	 * @param tag
	 *            태그숫자
	 * @return
	 */
	public boolean isTagExist(int tag)
	{
		return (tagList.indexOf(tag) >= 0);
	}

	/**
	 * 모든 태그 목록(collection)을 가져옵니다.
	 *
	 * @return
	 */
	public ArrayList<Tag> getTags()
	{
		return tags;
	}

	/**
	 * 해당 태그를 모두 리턴한다.
	 *
	 * @param tagNo
	 *            태그번호
	 * @return 태그 ArrayList
	 */
	public ArrayList<Tag> getTags(int tagNo)
	{
		ArrayList<Tag> tagList = new ArrayList<Tag>();

		if( tags != null )
		{
			for (Tag t : tags)
			{
				if (t.getTagNo() != tagNo) continue;

				tagList.add(t);
			}
		}

		return tagList;
	}

	/**
	 * 지정한 태그를 가져옵니다.
	 * <p>찾고자 하는 태그가 여러개인 경우는, <b>첫번째 발견된 태그</b> 1개만 가져옵니다.</p>
	 *
	 * @param tagNo 태그번호
	 * @return
	 */
	public Tag getTag(int tagNo)
	{
		if( tags != null )
		{
			for (Tag t : tags)
			{
				if (t.getTagNo() == tagNo) return t;
			}
		}

		return null;
	}

	/**
	 * 기본생성자 (통합코막유형으로 세팅합니다.)
	 */
	public MarcRecord()
	{
		this.marcType = MarcType.iKORMARC;
	}

	/**
	 * 기본생성자
	 *
	 * @param marcType
	 *            기본적으로 설정할 마크유형
	 */
	public MarcRecord(MarcType marcType)
	{
		this.marcType = marcType;
	}

	/**
	 * 마크 파서 생성자
	 *
	 * @param record
	 *            마크 스트링(RAW or XML)
	 * @param marcType
	 *            마크유형(KORMARC, MARC21)
	 * @param marcFormat
	 *            마크포맷(MARC, 파싱된 마크, MARCXML)
	 * @throws SMException
	 */
	public MarcRecord(String record, MarcType marcType, MarcFormat marcFormat) throws SMException
	{
		this.marcType = marcType;

		if (marcFormat == MarcFormat.MARC)
			parseMarc(record);
		else if (marcFormat == MarcFormat.PARSEDMARC)
			parseTagArray(record);
		else if (marcFormat == MarcFormat.MARCXML)
			parseMarcXml(record);

		matrType = getMaterialType(this.leader, this.marcType);
		if( tags != null )
			maxKey = tags.size();
	}

	/**
	 * MARC 문자열을 파싱해서 HashTable에 담는다
	 *
	 * @param marc
	 *            MARC RAW 문자열
	 * @throws MalformedMarcException
	 */
	private void parseMarc(String marc) throws MalformedMarcException
	{
		char tagDelimiter = Delimiter.TAG;

		if (StringUtil.isNullOrEmpty(marc))
			throw new MalformedMarcException("MARC is mal-formed!\r\nThe marc string was empty");

		if (marc.length() < 24)
			throw new MalformedMarcException("MARC is mal-formed!\r\nThe marc string has invalid length");

		if (marc.endsWith("&"))
			tagDelimiter = '%';

		leader = marc.substring(0, 24);

		int endOfDirectory = marc.indexOf(tagDelimiter);

		if (endOfDirectory < 24)
			throw new MalformedMarcException("MARC is mal-formed!\r\nInvalid directory length");

		String directory = marc.substring(24, endOfDirectory);
		String[] data = marc.substring(endOfDirectory + 1).split(new String(new char[] { tagDelimiter }));

		int currentIndex = 0;

		String tagString = null;
		for (int i = 0; i < directory.length(); i += 12)
		{
			tagString = directory.substring(i, i + 3);
			int tag = TypeConv.ToInt(tagString.replace(' ', '0'));

			Tag t;
			try
			{
				if (tag < 10)
					t = new Tag(tag, null, data[i / 12]);
				else
					t = new Tag(tag, data[i / 12].substring(0, 2), data[i / 12].substring(2));
			}
			catch (Exception e)
			{
				continue;
			}

			t.setCheckKey(StringUtil.padl(tag + "", 3, '0') + StringUtil.padl(currentIndex + "", 3, '0'));
			tags.add(t);

			currentIndex++;

			if (tagList.indexOf(tag) < 0) tagList.add(tag);
		}
	}

	/**
	 * 교열지 형태의 마크를 파싱해서 HashTable에 담는다
	 *
	 * @param marc raw마크 문자열
	 * @throws MalformedMarcException
	 */
	private void parseTagArray(String marc) throws MalformedMarcException
	{
		try
		{
			String[] token = marc.split("\\r?\\n");
			int currentIndex = 0;

			Tag t;
			for (String tagData : token)
			{
				// 비정상적인 TAG이면 Continue
				if (tagData == null || tagData.isEmpty() || tagData.length() < 4) continue;

				if (leader == null && tagData.length() == 24)
					leader = tagData;
				else
				{
					int tag = TypeConv.ToInt(tagData.substring(0, 3));

					if (tag < 10)
						t = new Tag(tag, null, tagData.substring(5));
					else
						t = new Tag(tag, tagData.substring(3, 5), tagData.substring(5));

					currentIndex++;

					t.setCheckKey(StringUtil.padl(tag + "", 3, '0') + StringUtil.padl(currentIndex + "", 3, '0'));
					tags.add(t);

					if (tagList.indexOf(tag) < 0) tagList.add(tag);
				}
			}
		}
		catch (Exception e)
		{
			throw new MalformedMarcException("MARC is mal-formed!\r\n", e);
		}
	}

	/**
	 * MarcXml로 된 마크를 파싱해서 Hashtable에 넣는다.
	 *
	 * @param xmlString
	 *            MARCXML 문자열
	 * @throws MalformedMarcException
	 */
	@SuppressWarnings("unchecked")
	private void parseMarcXml(String xmlString) throws MalformedMarcException
	{
		try
		{
			int currentIndex = 0;

			XmlDocument xml = new XmlDocument(xmlString);

			// node name 은 대소문자를 구분하며, marcXml에서 노드명은 전부 소문자이다.
			List<Element> list = ((Element) xml.selectSingleNode("record")).getChildren();

			Tag t;
			for( Element node : list)
			{
				if (node.getName().equals("leader"))
					leader = node.getText();
				else if (node.getName().equals("controlfield"))
				{
					int tagNo = TypeConv.ToInt(node.getAttributeValue("tag"));

					if (node.getText().length() > 0)
					{
						currentIndex++;

						t = new Tag(tagNo, null, node.getText());
						t.setCheckKey(StringUtil.padl(tagNo + "", 3, '0') + StringUtil.padl(currentIndex + "", 3, '0'));
						tags.add(t);

						if (tagList.indexOf(tagNo) < 0) tagList.add(tagNo);
					}
				}
				else if (node.getName().equals("datafield"))
				{
					char indi1 = ' ';
					char indi2 = ' ';

					int tagNo = TypeConv.ToInt(node.getAttributeValue("tag"));

					if (node.getAttribute("ind1") != null)
						indi1 = node.getAttributeValue("ind1").charAt(0);

					if (node.getAttribute("ind2") != null)
						indi2 = node.getAttributeValue("ind2").charAt(0);

					currentIndex++;

					t = new Tag(tagNo, indi1, indi2, node.getChildren());
					t.setCheckKey(StringUtil.padl(tagNo + "", 3, '0') + StringUtil.padl(currentIndex + "", 3, '0'));
					tags.add(t);

					if (tagList.indexOf(tagNo) < 0) tagList.add(tagNo);
				}
			}
		}
		catch (Exception e)
		{
			throw new MalformedMarcException("MARC is mal-formed! \r\n", e);
		}
	}

	/**
	 * Ascii 인코딩 문자열로 RAW마크 문자열을 반출한다.
	 * <blockquote><b>문자의 크기를 계산하는 방식</b>이 다르기 때문에 Unicode/ASCII 로 구별한다.</blockquote>
	 *
	 * @return 마크문자열
	 */
	public byte[] getAsciiMarc()
	{
		StringBuffer data = new StringBuffer();
		StringBuffer directory = new StringBuffer();

		if (maxKey == 0) maxKey = tags.size();

		int offset = 0;

		for (Tag t : tags)
		{
			if (t == null) continue;

			String tagData = handler.toAscii(t.getTagData());
			byte[] b = tagData.getBytes();

			directory.append(StringUtil.padl(t.getTagNo() + "", 3, '0'));
			directory.append(StringUtil.padl(b.length + "", 4, '0'));
			directory.append(StringUtil.padl(offset + "", 5, '0'));

			data.append(tagData);

			offset += b.length;
		}

		// 26 = leader(24) + TagDelimiter(1) + RecordDelimiter(1)
		int marcLength = 26 + directory.length() + data.toString().getBytes().length;

		// 25 = leader(24) + TagDelimiter(1)
		int dataOffset = 25 + directory.length();

		// leader 레코드길이와 9번째필드값을 ' '로, 그리고 데이터시작 Offset 다시계산
		leader = StringUtil.padl(marcLength + "", 5, '0') + leader.substring(5, 9) + ' ' + leader.substring(10, 12) + StringUtil.padl(dataOffset + "", 5, '0') + leader.substring(17);

		StringBuffer marc = new StringBuffer(leader);
		marc.append(directory);
		marc.append((char) Delimiter.TAG); // directory와 data사이는 TagDelimiter
		marc.append(data);
		marc.append((char) Delimiter.RECORD); // record끝에 RecordDelimiter

		return marc.toString().getBytes();
	}

	/**
	 * 유니코드로 RAW 마크문자열을 반출한다.
	 * <blockquote><b>문자의 크기를 계산하는 방식</b>이 다르기 때문에 Unicode/ASCII 로 구별한다.</blockquote>
	 *
	 * @return 유니코드 마크문자열
	 */
	public String getUnicodeMarc()
	{
		StringBuffer data = new StringBuffer();
		StringBuffer directory = new StringBuffer();

		if (maxKey == 0) maxKey = tags.size();

		int offset = 0;
		String tagData;
		for (Tag t : tags)
		{
			if (t == null) continue;

			tagData = handler.toUnicode(t.getTagData());
			directory.append(StringUtil.padl(t.getTagNo() + "", 3, '0'));
			directory.append(StringUtil.padl(tagData.length() + "", 4, '0'));
			directory.append(StringUtil.padl(offset + "", 5, '0'));
			data.append(tagData);
			offset += tagData.length();
		}

		// 26 = leader(24) + TagDelimiter(1) + RecordDelimiter(1)
		int marcLength = 26 + directory.length() + data.length();

		// 25 = leader(24) + TagDelimiter(1)
		int dataOffset = 25 + directory.length();

		// leader 레코드길이와 9번째필드값을 'a'로, 그리고 데이터시작Offset 다시계산
		leader = StringUtil.padl(marcLength + "", 5, '0') + leader.substring(5, 9) + 'a' + leader.substring(10, 12) + StringUtil.padl(dataOffset + "", 5, '0') + leader.substring(17);

		StringBuffer marc = new StringBuffer(leader);
		marc.append(directory);
		marc.append((char) Delimiter.TAG); // directory와 data사이는 TagDelimiter
		marc.append(data);
		marc.append((char) Delimiter.RECORD); // record끝에 RecordDelimiter

		return marc.toString();
	}

	/**
	 * 마크XML의 XmlDocument를 리턴한다.
	 *
	 * @return MARCXML XmlDocument
	 */
	public Document getMarcXmlDocument()
	{
		return getMarcXmlDocument("http://www.loc.gov/MARC21/slim");
	}

	/**
	 * 마크XML의 XmlDocument를 리턴한다.
	 *
	 * @return MARCXML XmlDocument
	 */
	public Document getMarcXmlDocument(String namespaceUrl)
	{
		Document doc = new Document();
		Element root = new Element("collection");
		Element tmp = null;

		if (!StringUtil.isNullOrEmpty(namespaceUrl))
		{
			Namespace ns = Namespace.getNamespace(namespaceUrl);
			root.addNamespaceDeclaration(ns);
		}

		doc.addContent(root);

		Element record = new Element("record");
		root = root.addContent(record);

		// leader
		tmp = new Element("leader");
		tmp.setText(this.leader);

		record.addContent(tmp);

		Element subField = null;
		for( Tag tag : this.tags)
		{
			if (tag.getTagNo() < 10)
			{
				tmp = new Element("controlfield");
				tmp.setText(tag.getTagData(false));
				tmp.setAttribute(new Attribute("tag", StringUtil.padl(tag.getTagNo() + "", 3, '0')));
			}
			else
			{
				tmp = new Element("datafield");
				for (int j = 0; j < tag.getSubFieldCount(); j++)
				{

					char sfCode = tag.getSubFields()[j].getCode();
					String sfData = tag.getSubFields()[j].getData();

					subField = new Element("subfield");
					subField.setText(sfData);
					subField.setAttribute(new Attribute("code", sfCode + ""));
					tmp.addContent(subField);
				}

				tmp.setAttribute(new Attribute("tag", StringUtil.padl(tag.getTagNo() + "", 3, '0')));
				tmp.setAttribute(new Attribute("ind1", tag.getIndi().charAt(0) + ""));
				tmp.setAttribute(new Attribute("ind2", tag.getIndi().charAt(1) + ""));
			}

			record.addContent(tmp);
		}
		return doc;
	}

	/**
	 * MARCXML을 문자열로 리턴한다.
	 *
	 * @return MARCXML문자열
	 */
	public String getMarcXml()
	{
		try
		{
			Document doc = getMarcXmlDocument();
			StringWriter writer = new StringWriter();
			XMLOutputter outp = new XMLOutputter();
			outp.output(doc, writer);
			return writer.getBuffer().toString();
		}
		catch (IOException e)
		{
		    e.printStackTrace(System.err);
			return "";
		}
	}

	/**
	 * 교열지 형식으로 표현된 마크를 가져옵니다.<br>
	 * 서브필드 구분자를 '$'를 사용해서 표현합니다.
	 *
	 * @return
	 */
	public String getParsedMarc()
	{
		return getParsedMarc("$", "");
	}

    /**
     * 교열지 형식으로 표현된 마크를 가져옵니다.
     *
     * @param subfieldDelimiterSymbole
     *            표현된 문자열에서 서브필드구분자를 표시하기 위한 문자입니다.
     *            <blockquote>
     *              &ltExample&gt; '▼' or '$' or ...
     *            </blockquote>
     * @param tagDelimiterSymbole
     *            표현된 문자열에서 태그 종단기호를 표시하기 위한 문자입니다.
     *            <blockquote>
     *              &ltExample&gt; '▲' or '&' or ...
     *            </blockquote>
     * @return
     */
	public String getParsedMarc(String subfieldDelimiterSymbole, String tagDelimiterSymbole)
	{
		StringBuilder sb = new StringBuilder();

		sb.append("LDR    ").append(this.getLeader()).append("\r\n");
		for( Tag t : this.tags)
		{
            sb.append(String.format("%03d ", t.getTagNo()));
            sb.append(t.getIndi() + " ");

            if (t.isControlTag())
                sb.append(t.getTagData() + tagDelimiterSymbole).append("\r\n");
            else
            {
                for( SubField sf : t.getSubFieldList())
                {
                    sb.append(subfieldDelimiterSymbole + sf.getCode() + sf.getData());
                }
                sb.append(tagDelimiterSymbole + "\r\n");
            }
		}
		return sb.toString();
	}

	/**
	 * 태그 추가
	 *
	 * @param tag
	 *            태그번호
	 * @param indi
	 *            지시기호
	 * @param data
	 *            태그데이터
	 */
	public void addTag(int tag, String indi, String data)
	{
		int i = 0;

		for (; i < tags.size(); i++)
		{
			Tag t = tags.get(i);

			if (tag < t.getTagNo()) break;
		}

		tags.add(i, new Tag(tag, indi, data));

		if (tagList.indexOf(tag) < 0) tagList.add(tag);
	}

	/**
	 * 태그 추가
	 *
	 * @param tag
	 */
	public void addTag(Tag tag)
	{
		int i = 0;

		int siz = tags.size();
		Tag t = null;
		for (; i < siz; i++)
		{
			t = tags.get(i);

			if (tag.getTagNo() < t.getTagNo()) break;
		}

		tags.add(i, tag);

		if (tagList.indexOf(tag.getTagNo()) < 0) tagList.add(tag.getTagNo());
	}

	/**
	 * 태그를 삭제합니다.
	 * <blockquote>여러개의 태그가 존재하는 경우, 첫번째로 일치하는 태그가 삭제됩니다.</blockquote>
	 *
	 * @param tag
	 *            태그번호
	 */
	public void deleteTag(int tag)
	{
		if (tagList.indexOf(tag) >= 0) tagList.remove((Object) tag);

		for (Tag t : tags)
		{
			if (t.getTagNo() == tag)
			{
				tags.remove(t);
				break;
			}
		}
	}

	/**
	 * 태그를 정렬합니다.
	 */
	public void sortTag()
	{
		for (int i = tags.size() - 1; i >= 0; i--)
		{
			for (int j = 0; j < i; j++)
			{
				Tag curTag = tags.get(j);

				String curTagString = StringUtil.padl(String.valueOf(curTag.getTagNo()), 3, '0') + curTag.getTagData();

				Tag nextTag = tags.get(j + 1);

				String nextTagString = StringUtil.padl(String.valueOf(nextTag.getTagNo()), 3, '0') + nextTag.getTagData();

				if (curTagString.compareTo(nextTagString) > 0)
				{
					tags.remove(j);
					tags.add(j + 1, curTag);
				}
			}
		}
	}

	/**
	 * 현재 객체의 상태를 초기화 상태로 정리합니다.
	 */
	public void Clear()
	{
		tags.clear();
		tags.trimToSize();

		tagList.clear();
		tagList.trimToSize();

		marcType = MarcType.iKORMARC;
		matrType = MaterialTypeConstant.MISSING;
		leader = null;
	}

	/**
	 * leader를 분석하여, KORMARC 여부를 가져옵니다.
	 * <blockquote>
	 * 단, 리더(leader) 정보만으로는  통합코막인지 그냥 코막인지 구별할 수 없습니다.<br>
	 * 단지, 코막유형인지 MARC21(USMARC)인지만이 식별가능합니다.
	 * </blockquote>
	 * @param leader
	 * @return
	 * @throws SMException
	 */
	public static boolean isKORMARC(String leader) throws MalformedMarcException
	{
		// 주의사항 : 리더만 보고서는 통합코막인지 그냥 코막인지 구별할 수 없다.
		if (StringUtil.isNullOrEmpty(leader) || leader.length() < 24)
			throw new MalformedMarcException("Argument 'leader' was empty or invalid length.");

		char leader18 = leader.charAt(18);
		char leader06 = leader.charAt(6);
		if ((leader18 == 'K' || leader18 == 'C') && leader06 != 'z') return true;
		return false;
	}

	/**
	 * 마크의 리더를 보고 자료유형을 판단한다
	 *
	 * @param leader
	 * @param marcType
	 * @return
	 */
	public static String getMaterialType(String leader, MarcType marcType)
	{
		if (marcType == MarcType.iKORMARC)
			return judgeiKORMARCMatrialType(leader);
		else
			return judgeMatrialType(leader);
	}

	/**
	 * 입력된 마크의 리더를 보고 자료유형을 판단한다.
	 * <pre>
	 * 마크유형이 KORMARC/MARC21 인 경우만 해당됩니다.
	 * 통합코막(iKORMARC)인 경우는 {@link #judgeiKORMARCMatrialType(String)}을 사용하십시요.
	 * </pre>
	 * @param strleader
	 */
	private static String judgeMatrialType(String strleader)
	{
		if (strleader == null || strleader.length() < 7)
		{
			return MaterialTypeConstant.BOOK;
		}

		char recType = strleader.charAt(6);
		char bibLevel = strleader.charAt(7);

		switch (recType)
		{
			case 'w': /* 국내서 고서 처리위해 1998/05/14 */
			case 'h':
			case 'a':
				switch (bibLevel)
				{
					case 'a':
					case 'c':
					case 'm':
						return MaterialTypeConstant.BOOK;
					case 'b':
					case 's':
						return MaterialTypeConstant.SERIAL;
					default:
						return MaterialTypeConstant.BOOK;
				}
			case 'b':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'd':
					case 'm':
						return MaterialTypeConstant.ARCMAN;
					default:
						return MaterialTypeConstant.BOOK;
				}
			case 'm':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'm':
					case 's':
						return MaterialTypeConstant.COMFIL;
					default:
						return MaterialTypeConstant.BOOK;
				}
			case 'e':
			case 'f':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'm':
					case 's':
						return MaterialTypeConstant.MAPS;
					default:
						return MaterialTypeConstant.BOOK;
				}
			case 'c':
			case 'd':
			case 'i':
			case 'j':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'm':
					case 's':
						return MaterialTypeConstant.MUSIC;
					default:
						return MaterialTypeConstant.BOOK;
				}
			case 'g':
			case 'k':
			case 'o':
			case 'r':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'd':
					case 'm':
						return MaterialTypeConstant.VISMAT;
					default:
						return MaterialTypeConstant.BOOK;
				}
			default:
				return MaterialTypeConstant.BOOK;
		}
	}

	/**
	 * 입력된 마크의 리더를 보고 자료유형을 판단한다.
	 * <pre>
	 * 마크유형이 통합코막(iKORMARC) 인 경우만 해당됩니다.
	 * KORMARC/MARC21 인 경우는 {@link #judgeMatrialType(String)}을 사용하십시요.
	 * </pre>
	 * @param strleader
	 */
	private static String judgeiKORMARCMatrialType(String strleader)
	{
		if (strleader == null || strleader.length() < 7)
		{
			return MaterialTypeConstant.BOOK;
		}

		char recType = strleader.charAt(6);
		char bibLevel = strleader.charAt(7);

		switch (recType)
		{
			case 'a':
			case 't':
				switch (bibLevel)
				{
					case 'a':
					case 'c':
					case 'd':
					case 'm':
						return MaterialTypeConstant.BOOK;
					case 'b':
					case 's':
					case 'i':
						return MaterialTypeConstant.CONTMAT;
					default:
						return MaterialTypeConstant.MIXEDMAT;
				}
			case 'w':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'd':
					case 'm':
					case 'i':
					case 's':
						return MaterialTypeConstant.RAREBOOK;
					default:
						return MaterialTypeConstant.MIXEDMAT;
				}
			case 'e':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'd':
					case 'i':
					case 'm':
					case 's':
						return MaterialTypeConstant.MAPS;
					default:
						return MaterialTypeConstant.MIXEDMAT;
				}
			case 'f':
				switch (bibLevel)
				{
					case 'a':
					case 'c':
					case 'd':
					case 'm':
						return MaterialTypeConstant.MAPS;
					default:
						return MaterialTypeConstant.MIXEDMAT;
				}
			case 'c':
			case 'i':
			case 'j':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'd':
					case 'i':
					case 'm':
					case 's':
						return MaterialTypeConstant.MUSIC;
					default:
						return MaterialTypeConstant.MIXEDMAT;
				}
			case 'd':
				switch (bibLevel)
				{
					case 'a':
					case 'c':
					case 'd':
					case 'm':
						return MaterialTypeConstant.MUSIC;
					default:
						return MaterialTypeConstant.MIXEDMAT;
				}
			case 'g':
			case 'k':
			case 'o':
			case 'r':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'd':
					case 'i':
					case 'm':
					case 's':
						return MaterialTypeConstant.VISMAT;
					default:
						return MaterialTypeConstant.MIXEDMAT;
				}
			case 'm':
				switch (bibLevel)
				{
					case 'a':
					case 'b':
					case 'c':
					case 'd':
					case 'i':
					case 'm':
					case 's':
						return MaterialTypeConstant.ELEMAT;
					default:
						return MaterialTypeConstant.MIXEDMAT;
				}
			case 'p':
				return MaterialTypeConstant.MIXEDMAT;
			default:
				return MaterialTypeConstant.MIXEDMAT;
		}
	}

	public static void main(String[] args) throws Exception
	{
		String fileName = "";
		fileName = "D:/sample_marcXML_A_raw.mrc";
		// fileName ="D:/sample_marcXML_dli_raw.mrc";

		BufferedReader in = new BufferedReader(new FileReader(fileName));

		String strLine = null;
		StringBuilder sb = new StringBuilder();
		while ((strLine = in.readLine()) != null)
		{
			sb.append(strLine).append("\r\n");
		}
		in.close();

		String marc = null;
		marc = sb.toString();
		MarcRecord r = null;

		r = new MarcRecord(marc, MarcType.KORMARC, MarcFormat.MARC);
		// r = new MarcRecord(marc, MarcType.KORMARC, MarcFormat.MARC);

		ArrayList<Tag> tagList = r.getTags();

		for (int i = 0; i < tagList.size(); i++)
		{

			Tag t = tagList.get(i);

			System.out.print(String.format("%03d ", t.getTagNo()));
			System.out.print(t.getIndi() + " ");

			if (t.isControlTag())
				System.out.println(t.getTagData());
			else
			{
				SubField[] s = t.getSubFields();
				for (int j = 0; j < s.length; j++)
				{
					System.out.print("$" + s[j].getCode() + s[j].getData());
				}
				System.out.println("");
			}
		}

		System.out.println("=============================================");
		System.out.println("=============================================");
		System.out.println("PARSED MARC Style");
		System.out.println("=============================================");
		System.out.println("=============================================");
		System.out.println(r.getParsedMarc());
		System.out.println("");
		System.out.println("");

		System.out.println("=============================================");
		System.out.println("=============================================");
		System.out.println("PARSED MARC Style 22");
		System.out.println("=============================================");
		System.out.println("=============================================");
		System.out.println(r.getParsedMarc("▼", "▲"));
		System.out.println("");
		System.out.println("");

		System.out.println("=============================================");
		System.out.println("=============================================");
		System.out.println("PARSED MARC Style 22,  after sort");
		System.out.println("=============================================");
		System.out.println("=============================================");
		r.sortTag();
		System.out.println(r.getParsedMarc("▼", "▲"));
		System.out.println("");
		System.out.println("");

		System.out.println("=============================================");
		System.out.println("=============================================");
		System.out.println("RAW MARC Style - unicode");
		System.out.println("=============================================");
		System.out.println("=============================================");
		System.out.println(r.getUnicodeMarc());
		System.out.println("");
		System.out.println("");

		r = null;

	}

}
